package com.jwgcs.runningDog.controller;

import com.jwgcs.runningDog.entity.Param;
import com.jwgcs.runningDog.util.DateUtil;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.*;
import java.net.URL;
import java.util.*;

public class RunningDogController implements Controller {
    public static final int PARAM_ROW = 500;
    public static final boolean debug = false;
    public static int[][] PARAMS = new int[PARAM_ROW][7];
    private List<String> randomsToSave = new ArrayList<>();
    private boolean start = false;//启动状态
    private int page = 0;
    int num;//随机数组长度
    int ruleNum;//连续数
    int paramRow;//参数行数
    int paramCol;//参数列数
    int resultIndex = 0;
    long runTimes = 0l;
    int[] temp;
    int maxRule;
    Timer freshTimer = new Timer("freshTimer",true);
    TimerTask task = new FreshTask();
    long startTime;
    String dateStr = "";


    @FXML
    private TableView<Map<String, Object>> paramTableView;

    @FXML
    private TableView<Map<String, Object>> resultTableView;
    @FXML
    private TextField numText;
    @FXML
    private TextField ruleNumText;
    @FXML
    private Button startButton;
    @FXML
    private Button stopButton;
    @FXML
    private Text runTimesText;
    @FXML
    private Text maxRuleText;
    @FXML
    private Text timeText;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        List<Param> list = new ArrayList<>();
        for (int i = 1; i <= PARAM_ROW; i++) {
            Param p = new Param();
            p.setId(i);
            list.add(p);
        }
        ObservableList<?> items = FXCollections.observableList(list);
        paramTableView.setItems((ObservableList<Map<String, Object>>) items);
        initResult();
        paramTableView.setEditable(true);
        stopButton.setDisable(true);
        freshTimer.schedule(task, 2000, 1000);
    }

    private void initResult(){
        List<Param> result = new ArrayList<>();
        ObservableList<?> items1 = FXCollections.observableList(result);
        resultTableView.setItems((ObservableList<Map<String, Object>>) items1);
    }

    @FXML
    public void start(ActionEvent event) {
        startButton.setDisable(true);
        stopButton.setDisable(false);
        resultIndex = 0;
        maxRule = 0;
        runTimes = 0;
        startTime = System.currentTimeMillis();
        dateStr = DateUtil.dateToString(new Date(startTime), "yyyyMMddHHmmss");
        freshText();
        initResult();

        start = true;

        num = Integer.valueOf(numText.getText());
        ruleNum = Integer.valueOf(ruleNumText.getText());
        paramRow = PARAM_ROW;
        for (int i = 0; i < PARAM_ROW; i++) {
            if (PARAMS[i][0] <= 0) {
                paramRow = i;
                break;
            }
        }

        paramCol = 7;
        for (int i = 0; i < 7; i++) {
            if (PARAMS[0][i] <= 0) {
                paramCol = i;
                break;
            }
        }
        temp = new int[num];//初始化样例数组1--num
        for (int i = 0; i < num; i++) {
            temp[i] = i + 1;
        }

        Thread thread = new Thread(new Runner(this));
        thread.start();

    }

    public void run(){
        while(start){
            Param p = calculate();
            if(p!=null){
                resultTableView.getItems().add(p);
                if(resultIndex%100 == 0){
                    try {
                        saveResult(resultTableView.getItems());
                    } catch (IOException e) {
                        e.printStackTrace();
                        stop(null);
                    }
                    initResult();
                    page++;
                }
            }
        }
    }

    public void saveResult(ObservableList<Map<String, Object>> list) throws IOException{
        File file = new File(dateStr+"-result-"+page+".txt");
        FileWriter fw = new FileWriter(file);
        StringBuilder sb = new StringBuilder();
        for(Map<String, Object> map : list){
            sb.append(map.get("id")).append("\t");
            sb.append(map.get("r0")).append("\t");
            sb.append(map.get("r1")).append("\t");
            sb.append(map.get("r2")).append("\t");
            sb.append(map.get("r3")).append("\t");
            sb.append(map.get("r4")).append("\n");
        }
        fw.write(sb.toString());
        fw.close();
        if(debug){
            file = new File(dateStr+"-randoms-"+page+".txt");
            fw = new FileWriter(file);
            sb = new StringBuilder();
            for(String s : randomsToSave){
                sb.append(s);
            }
            fw.write(sb.toString());
            fw.close();
        }

    }

    @FXML
     protected void stop(ActionEvent event) {
        start = false;
        startButton.setDisable(false);
        stopButton.setDisable(true);
        freshText();
        try {
            saveResult(resultTableView.getItems());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 计算逻辑
     * @return
     */
    private Param calculate() {
        runTimes++;
        Map<Integer, Integer> map = new HashMap<>();
        int[][] randoms = new int[paramRow + 3][num];
        for (int i = 0; i < paramRow + 3; i++) {
            int[] r = Arrays.copyOf(temp, num);
            r = shuffle(r);
            randoms[i] = r;
        }

        if(debug && runTimes%10000==0){
            print(runTimes,randoms);//打印
        }
        out:
        for(int i =0 ; i < num ; i++){
            for(int j = 0 ; j < paramCol ; j++){
                if(randoms[paramRow - 1][i]==PARAMS[paramRow - 1][j]){
                    continue out;
                }
            }
            map.put(i,1);
        }

        for (int i = 1; i < paramRow; i++) {
            Set<Integer> set = map.keySet();
            Set<Integer> set1 = new HashSet<>(set.size());
            for(int k : set){
                set1.add(k);
            }
            int first = -1;
            out:
            for(int k : set1){
                if(first==-1){
                    first = k;//记录第一个列数
                }
                for(int j = 0 ; j < paramCol ; j++){
                    if(randoms[paramRow-1-i][k]==PARAMS[paramRow-1-i][j]){
                        map.remove(k);
                        continue out;
                    }
                }
                map.put(k,i+1);
            }
            if(map.isEmpty()||i == paramRow - 1){
                if(!map.isEmpty()){
                    i++;
                }
                if(i>=ruleNum){
                    if(debug){
                        print(runTimes,randoms);//打印
                    }
                    return makeResult(randoms,first,i);
                }else{
                    return null;
                }
            }
        }

        return null;
    }

    public Param makeResult(int[][] randoms,int col,int max){
        Param p = new Param();
        p.setId(++resultIndex);
        if(max>maxRule){
            maxRule = max;
        }
        p.put("r0", max);
        p.put("r1",randoms[paramRow][col]);
        p.put("r2",randoms[paramRow+1][col]);
        p.put("r3",randoms[paramRow+2][col]);
        p.put("r4",runTimes);
        return p;
    }

    public int[] shuffle(int[] arr) {
        int[] arr2 = new int[arr.length];
        int count = arr.length;
        int cbRandCount = 0;// 索引
        int cbPosition = 0;// 位置
        int k = 0;
        do {
            Random rand = new Random();
            int r = count - cbRandCount;
            cbPosition = rand.nextInt(r);
            arr2[k++] = arr[cbPosition];
            cbRandCount++;
            arr[cbPosition] = arr[r - 1];// 将最后一位数值赋值给已经被使用的cbPosition
        } while (cbRandCount < count);
        return arr2;
    }

    public void print(long random,int[][] randoms){
        StringBuilder sb = new StringBuilder();
        sb.append("runtime:").append(random).append("\n");
        for (int i = 0; i < paramRow + 3; i++) {
            sb.append("p").append(i + 1).append(":\t");
            for (int j = 0; j < num; j++) {
                sb.append(randoms[i][j]).append("\t");
            }
            sb.append("\n");
        }
        sb.append("\n");
        randomsToSave.add(sb.toString());
    }

    public void freshText(){
        runTimesText.setText("" + runTimes);
        maxRuleText.setText("" + maxRule);
        if(start)
            timeText.setText(""+(System.currentTimeMillis() - startTime)/1000 +"s");
    }

    private class FreshTask extends TimerTask{

        @Override
        public void run() {
            freshText();
        }
    }

    @FXML
    protected void input(ActionEvent event) throws Exception {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("请选择参数文件");
        File file = fileChooser.showOpenDialog(new Stage());
        if(file==null)
            return;
        FileReader reader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(reader);
        int i = 0;
        PARAMS = new int[PARAM_ROW][7];
        List<Param> list = new ArrayList<>();
        String str = bufferedReader.readLine();
        while(str != null && str.length() >0){
            String ps[] = str.split("\t");
            for(int j = 0;j<ps.length && j < 7;j++){
                PARAMS[i][j] = Integer.valueOf(ps[j].trim());
            }
            str = bufferedReader.readLine();
            i++;
        }

        for(i = 0 ;i<PARAM_ROW;i++){
            Param p = new Param();
            p.setId(i+1);
            for(int j = 1;j<=7;j++){
                p.put("p"+j,PARAMS[i][j-1]);
            }
            list.add(p);
        }
        ObservableList<?> items = FXCollections.observableList(list);
        paramTableView.setItems((ObservableList<Map<String, Object>>) items);
    }



}
